//=====================================================================
//    COPYRIGHT(C) Innobeam
//    ALL RIGHTS RESERVED
//=====================================================================
//Filename    : TR_control.v rev 1.0
//Created On  : 2018-01-10
//Author      : shilong.zhang
//Description :	Read and write lan9252 PRAM for required number of word
//Include     :
//Modification:
//=====================================================================
module TR_control
(
	iClk,
	iRst_n,

	//to lan9252.v ports
	ovAddrSel,	//2bit address for sellect direct reg or CSR or Pram mode
	ovAddr,		//16bit for lan9252.v
	ovData,		//32bit for lan9252(data written to dreg or CSR) to lan9252.v
	oWr,		//output,write enable signal lan9252.v
	oRd,		//output,read enable signal lan9252.v
	iLan_Done,	//input operation is done from lan9252.v
	iReady,		//input LAN9252 next PRAM operation ready, from lan9252.v
	ovNum_pram,	//4bit output Rx/Tx dword number
	ivData,		//32bit input lan9252 data(commom register or CSR register);

	// others
	oCmdStart,	//1bit;1:to unpack module(one clock high pulse,new read CMD start);
	oComm_state	//communication state(1:communication OK;0:ERROR);
);

//========================================================================
//    parameter
//========================================================================
//difine whether to check communication status。1:check;0:not check
parameter ENABLE_RD_STA = 1;
//AL_STATUS register address
parameter	ADDR_ALS = 16'h0130; // 0x130
//ESC_DL_STATUS register address
parameter	ADDR_DLS = 16'h0110; // 0x110
//tx and rx number
parameter		TX_NUM	 = 10'd11;//dowrd number to be sent to EtherCAT
parameter		RX_NUM	 = 10'd5;//dword number to be read from EhterCAT

//state machine parameter
parameter	RCMD = 3'b001;	//read PRAM data
parameter	WCMD = 3'b010;	//write PRAM data
parameter	CHMD = 3'b011;	//change mode to CSR
parameter	RALS = 3'b100;	//read Al status CSR register
parameter	RDLS = 3'b101;	//read ESC dl status CSR register
parameter	CHMP = 3'b110;	//change mode to pram

//Tx and Rx address
parameter  	RD_ADDR = 16'h1000;
parameter   WR_ADDR = 16'h1800;

//========================================================================
//    port
//========================================================================
input			iClk;
input			iRst_n;
//to lan9252.v ports
output	[1:0]	ovAddrSel;
output	[15:0]	ovAddr;
output	[31:0]	ovData;
output			oWr;
output			oRd;
input			iLan_Done;
input			iReady;
output	[3:0]	ovNum_pram;
input	[31:0]	ivData;
// others
output			oCmdStart;
output			oComm_state;

//========================================================================
//    signal
//========================================================================
reg		[1:0]	rvAddrSel;	//lan9252 work mode sellect
reg		[15:0]	rvAddr;		//lan9252 address
reg		[31:0]	rvData;		//output 32bit data to lan9252(reserved)
reg				rWr,rRd;	//write enable and read enable
reg		[3:0]	rvNum_pram;	//number of read and write PRAM operation
reg				rCmdStart;	//rx CMD start
reg				rOp_flag;	//flag:1:write or read enable pulse has
							//been sent;0:write or read enable pulse
							//not generated
reg		[2:0]	rvState_c;		//current state register
reg		[2:0]	rvState_n;		//next state register
reg		[9:0]	rvLen,rvTxLen;	//receive length and transmit length
reg				rLenSubEn;	//flag,HIGH means:one PRAM read operation
							//is done(8 dwords),rx length reg should
							//subtract 8;
reg				rTxLenSubEn;	//flag,HIGH means:one PRAM transmit operation
								//is done(8 dwords),tx length reg should
								//subtract 8;
reg				rLastFrame;	//flag,HIGH means this is the last PRAM
							//read or write operation(rvLen or rvTxlen number)
//reg		[8:0]	rvAddrCnt;
//reg		[8:0]	rvAddrCntTx;
reg		[10:0]	rvAddrCnt;
reg		[10:0]	rvAddrCntTx;
reg		[31:0]	rvAl_status;
reg		[31:0]	rvEsc_dl_status;

wire			wRcmd2wcmd_start;
wire			wWcmd2rcmd_start;
wire			wChmd2rals_statr;
wire			wRals2rdls_statr;
wire			wRdls2chmp_statr;
wire			wChmp2rcmd_start;

//========================================================================
//    module body
//========================================================================

//output assign
assign	ovAddrSel = rvAddrSel;
assign	ovAddr = rvAddr;
assign	ovData = 16'h0000;
assign	oWr = rWr;
assign	oRd = rRd;
assign	ovNum_pram = rvNum_pram;
assign	oCmdStart = rCmdStart;

// store the contents AL_STATUS and ESC_DL_STATUS register
always @ (posedge iClk or negedge iRst_n) begin
	if(iRst_n == 1'b0)begin
		rvAl_status <= 32'h00000000;
		rvEsc_dl_status <= 32'h00000000;
		end
	else begin
		if((ovAddr == ADDR_ALS) && (iLan_Done == 1'b1))begin
			rvAl_status <= ivData;
			end
		else if((ovAddr == ADDR_DLS) && (iLan_Done == 1'b1))begin
			rvEsc_dl_status <= ivData;
			end
		else begin
			rvAl_status <= rvAl_status;
			rvEsc_dl_status <= rvEsc_dl_status;
			end
		end
	end

// rvAl_status[3:0] == 4'd8 means ESC is in operation state;
// rvEsc_dl_status[9] == 1'b1 means port0 communication established
assign	oComm_state = ENABLE_RD_STA ? ((rvEsc_dl_status[9] == 1'b1)
									&& (rvAl_status[3:0] == 4'd8))
									: 1'b1;

// storing RX length of word
always @(posedge iClk or negedge iRst_n)begin
	if(iRst_n == 1'b0)begin
		rvLen <= RX_NUM;
		end
	else begin
		if(rvState_c == WCMD)begin
			rvLen <= RX_NUM;
			end
		else begin
			if(rLenSubEn == 1'b1)begin
				rvLen <= rvLen - 10'd8;
				end
			else begin
				rvLen <= rvLen;
				end
			end
		end
	end

// generate RX address of LAN9252 pram
always @(posedge iClk or negedge iRst_n)begin
	if(iRst_n == 1'b0)begin
		rvAddrCnt <= 11'd0;
		end
	else begin
		if(rvState_c == WCMD)begin
			rvAddrCnt <= 11'd0;
			end
		else if(rvState_c == RCMD)begin
			if(rRd == 1'b1)begin
				rvAddrCnt <= rvAddrCnt + 11'h20;
				end
			else begin
				rvAddrCnt <= rvAddrCnt;
				end
			end
		end
	end

// storing TX length of word
always @(posedge iClk or negedge iRst_n)begin
	if(iRst_n == 1'b0)begin
		rvTxLen <= TX_NUM;
		end
	else begin
		if(rvState_c == RCMD)begin
			rvTxLen <= TX_NUM;
			end
		else begin
			if(rTxLenSubEn == 1'b1)begin
				rvTxLen <= rvTxLen - 10'd8;
				end
			else begin
				rvTxLen <= rvTxLen;
				end
			end
		end
	end

// generate TX address for LAN9252 pram
always @(posedge iClk or negedge iRst_n)begin
	if(iRst_n == 1'b0)begin
		rvAddrCntTx <= 11'd0;
		end
	else begin
		if(rvState_c == RCMD)begin
			rvAddrCntTx <= 11'd0;
			end
		else if(rvState_c == WCMD)begin
			if(rWr == 1'b1)begin
				rvAddrCntTx <= rvAddrCntTx + 11'h20;
				end
			else begin
				rvAddrCntTx <= rvAddrCntTx;
				end
			end
		end
	end

//==============================================================================
// State machine description:
// RCMD wRcmd2wcmd_start==1 -> WCMD
// WCMD wWcmd2rcmd_start==1 -> RCMD
//===============================================================================

//第一段：同步时序always模块，格式化描述次态寄存器迁移到现态寄存器(不需更改）
always@(posedge iClk or negedge iRst_n)begin
	if(!iRst_n)begin
		rvState_c <= RCMD;
		end
	else begin
		rvState_c <= rvState_n;
		end
	end

//第二段：组合逻辑always模块，描述状态转移条件判断
always@(*)begin
case(rvState_c)
RCMD:begin
	if(wRcmd2wcmd_start)begin // iLan_Done & rLastFrame
		rvState_n = WCMD;
		end
	else begin
		rvState_n = rvState_c;
		end
	end
WCMD:begin
	if((!ENABLE_RD_STA) && wWcmd2rcmd_start)begin // iLan_Done & rLastFrame
		rvState_n = RCMD;
		end
	else if(ENABLE_RD_STA && wWcmd2rcmd_start)begin // iLan_Done & rLastFrame
		rvState_n = CHMD;
		end
	else begin
		rvState_n = rvState_c;
		end
	end
CHMD:begin
	if(wChmd2rals_statr)begin // iReady
		rvState_n = RALS;
		end
	else begin
		rvState_n = rvState_c;
		end
	end
RALS:begin
	if(wRals2rdls_statr)begin // iLan_Done
		rvState_n = RDLS;
		end
	else begin
		rvState_n = rvState_c;
		end
	end
RDLS:begin
	if(wRdls2chmp_statr)begin // iLan_Done
		rvState_n = CHMP;
		end
	else begin
		rvState_n = rvState_c;
		end
	end
CHMP:begin
	if(wChmp2rcmd_start)begin // iReady
		rvState_n = RCMD;
		end
	else begin
		rvState_n = rvState_c;
		end
	end
default:begin
	rvState_n = RCMD;
	end
endcase
end

//第三段：设计转移条件
assign wRcmd2wcmd_start = ((rvState_c == RCMD) && (iLan_Done == 1'b1) && (rLastFrame == 1'b1));
assign wWcmd2rcmd_start = ((rvState_c == WCMD) && (iLan_Done == 1'b1) && (rLastFrame == 1'b1));
assign wChmd2rals_statr = ((rvState_c == CHMD) && (iReady == 1'b1));
assign wRals2rdls_statr = ((rvState_c == RALS) && (iLan_Done == 1'b1));
assign wRdls2chmp_statr = ((rvState_c == RDLS) && (iLan_Done == 1'b1));
assign wChmp2rcmd_start = ((rvState_c == CHMP) && (iReady == 1'b1));

//第四段：同步时序always模块，格式化描述寄存器输出（可有多个输出）
always @(posedge iClk or negedge iRst_n)begin
if(iRst_n == 1'b0)begin
	rvAddr <=16'd0;      //初始化
	rOp_flag <= 1'b0;
	rRd <= 1'b0;
	rWr <= 1'b0;
	rvNum_pram <= 4'd0;
	rLenSubEn <= 1'b0;
	rTxLenSubEn <= 1'b0;
	rLastFrame <= 1'b0;
	rvAddrSel <= 2'b10; //PRAM mode
	end
else begin
	case(rvState_c)
	RCMD:begin
		rvAddrSel <= 2'b10; //PRAM mode
		rTxLenSubEn <= 1'b0;
		if((iReady == 1'b1)&&(rOp_flag == 1'b0))begin
			rRd <= 1'b1;
			rWr <= 1'b0;
			rOp_flag <= 1'b1;
			if(rvLen>8)begin
				rvAddr <= (RD_ADDR | {5'h00,rvAddrCnt});
				rvNum_pram <= 4'd8;
				rLenSubEn <= 1'b1;
				rLastFrame <= 1'b0;
				end
			else begin
				rvAddr <= (RD_ADDR | {5'h00,rvAddrCnt});
				rvNum_pram <= rvLen[3:0];
				rLenSubEn <= 1'b0;
				rLastFrame <= 1'b1;
				end
			end
		else begin
			rvAddr <= rvAddr;
			rRd <= 1'b0;
			rvNum_pram <= rvNum_pram;
			rWr <= 1'b0;
			rLenSubEn <= 1'b0;
			rLastFrame <= rLastFrame;
			if(iLan_Done==1'b1)begin
				rOp_flag <= 1'b0;
				end
			else begin
				rOp_flag <= rOp_flag;
				end
			end
		end
	WCMD:begin
		rvAddrSel <= 2'b10; //PRAM mode
		rLenSubEn <= 1'b0;
		if((iReady == 1'b1)&&(rOp_flag == 1'b0))begin
			rRd <= 1'b0;
			rWr <= 1'b1;
			rOp_flag <= 1'b1;
			if(rvTxLen > 4'd8)begin
				rvAddr <= (WR_ADDR | {5'h00,rvAddrCntTx});
				rvNum_pram <= 4'd8;
				rTxLenSubEn <= 1'b1;
				rLastFrame <= 1'b0;
				end
			else begin
				rvAddr <= (WR_ADDR | {5'h00,rvAddrCntTx});
				rvNum_pram <= rvTxLen[3:0];
				rTxLenSubEn <= 1'b0;
				rLastFrame <= 1'b1;
				end
			end
		else begin
			rvAddr <= rvAddr;
			rRd <= 1'b0;
			rvNum_pram <= rvNum_pram;
			rWr <= 1'b0;
			rTxLenSubEn <= 1'b0;
			rLastFrame <= rLastFrame;
			if(iLan_Done==1'b1)begin
				rOp_flag <= 1'b0;
				end
			else begin
				rOp_flag <= rOp_flag;
				end
			end
		end
	CHMD:begin
		rTxLenSubEn <= 1'b0;
		rvAddr <=16'd0;
		rOp_flag <= 1'b0;
		rRd <= 1'b0;
		rWr <= 1'b0;
		rvNum_pram <= 4'd0;
		rLenSubEn <= 1'b0;
		rTxLenSubEn <= 1'b0;
		rLastFrame <= 1'b0;
		if(iReady == 1'b1)begin
			rvAddrSel <= 2'b01; //CSR mode
			end
		else begin
			rvAddrSel <= rvAddrSel; //CSR mode
			end
		end
	RALS:begin
		rvAddrSel <= rvAddrSel; //PRAM mode
		rTxLenSubEn <= 1'b0;
		if((iReady == 1'b1)&&(rOp_flag == 1'b0))begin
			rRd <= 1'b1;
			rWr <= 1'b0;
			rOp_flag <= 1'b1;
			rvAddr <= ADDR_ALS;
			end
		else begin
			rvAddr <= rvAddr;
			rRd <= 1'b0;
			rvNum_pram <= 4'd0;
			rWr <= 1'b0;
			rLenSubEn <= 1'b0;
			rLastFrame <= rLastFrame;
			if(iLan_Done==1'b1)begin
				rOp_flag <= 1'b0;
				end
			else begin
				rOp_flag <= rOp_flag;
				end
			end
		end
	RDLS:begin
		rvAddrSel <= rvAddrSel; //PRAM mode
		rTxLenSubEn <= 1'b0;
		if((iReady == 1'b1)&&(rOp_flag == 1'b0))begin
			rRd <= 1'b1;
			rWr <= 1'b0;
			rOp_flag <= 1'b1;
			rvAddr <= ADDR_DLS;
			end
		else begin
			rvAddr <= rvAddr;
			rRd <= 1'b0;
			rvNum_pram <= 4'd0;
			rWr <= 1'b0;
			rLenSubEn <= 1'b0;
			rLastFrame <= rLastFrame;
			if(iLan_Done==1'b1)begin
				rOp_flag <= 1'b0;
				end
			else begin
				rOp_flag <= rOp_flag;
				end
			end
		end
	CHMP:begin
		rvAddrSel <= 2'b10; //PRAM mode
		rTxLenSubEn <= 1'b0;
		rvAddr <=16'd0;
		rOp_flag <= 1'b0;
		rRd <= 1'b0;
		rWr <= 1'b0;
		rvNum_pram <= 4'd0;
		rLenSubEn <= 1'b0;
		rTxLenSubEn <= 1'b0;
		rLastFrame <= 1'b0;
		end
	default:begin
		rvAddr <= rvAddr;
		rRd <= 1'b0;
		rvNum_pram <= rvNum_pram;
		rWr <= 1'b0;
		rLenSubEn <= 1'b0;
		rTxLenSubEn <= 1'b0;
		rLastFrame <= 1'b0;
		end
	endcase
	end
end

//=================================END of state machine===================================
//generate rCmdStart signal(1 clock high pulse)
always @(posedge iClk or negedge iRst_n)begin
	if(iRst_n == 1'b0)begin
		rCmdStart <= 1'b0;//output to unpack(1:unpack rcv CMD;0:unpack rcv Data)
		end
	else begin
		rCmdStart <= wWcmd2rcmd_start;//output to unpack(1:begin to receive CMD;)
		end
	end

endmodule

